import java.io.BufferedWriter;
import java.io.FileWriter;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.TreeMap;


public class IDSAnomalyDetector {

	/*
	 * fields
	 */
	private IDSModelBuilderLogic trainingModel;
	private IDSModelBuilderLogic testingModelForMe;
	private IDSModelBuilderLogic testingModelForOther;

	private TreeMap<Integer, Integer> mapForDistanceOnFrequencyForMe;//key:distance (x-axis), value: freq (y-axis)
	private TreeMap<Integer, Integer> mapForDistanceOnNGramForMe;//key:distance (x-axis), value: freq (y-axis)
	
	private TreeMap<Integer, Integer> mapForDistanceOnFrequencyForOther;
	private TreeMap<Integer, Integer> mapForDistanceOnNGRamForOther;
	
	
	/*
	 * constructor
	 */
	public IDSAnomalyDetector(){
	
	}
	
	/*
	 * methods
	 */
	public void setTrainingData(String trainingDataLogDirName){
		LogAnalyzer log = new LogAnalyzer(trainingDataLogDirName);
		LinkedList<Event> eventList = log.getEventList();
		this.trainingModel = new IDSModelBuilderLogic(eventList);
	}
	
	public void setTestingDataForMe(String testingDataForMeLogDirName){
		LogAnalyzer log = new LogAnalyzer(testingDataForMeLogDirName);
		LinkedList<Event> eventList = log.getEventList();
		this.testingModelForMe = new IDSModelBuilderLogic(eventList);
	}
	
	public void setTestingDataForOther(String testingDataForOtherLogDirName){
		LogAnalyzer log = new LogAnalyzer(testingDataForOtherLogDirName);
		LinkedList<Event> eventList = log.getEventList();
		this.testingModelForOther = new IDSModelBuilderLogic(eventList);
	}
	
	
	
	
	
	
	private TreeMap<Integer,Integer> createMapForDistanceOnNGram(IDSModelBuilderLogic testingModel){
		TreeMap<Integer,Integer> mapForDistanceOnNGram = new TreeMap<Integer, Integer>();
		
		//get mapForNGramScore
		LinkedHashMap<String, Integer> mapForNGramScoreTRAINING_MODEL = this.trainingModel.getMapForNGramScore();
		LinkedHashMap<String, Integer> mapForNGramScoreTESTING_ME = testingModel.getMapForNGramScore();
		
		Iterator ite = mapForNGramScoreTRAINING_MODEL.entrySet().iterator();
		while(ite.hasNext()){
			Map.Entry entry = (Map.Entry)ite.next();
			String key = (String)entry.getKey();
			int scoreInModel = (Integer)entry.getValue();
			int distance = 0;
			if(mapForNGramScoreTESTING_ME.containsKey(key)){
				int scoreInTestingMe = mapForNGramScoreTESTING_ME.get(key);
				distance = Math.abs(scoreInModel-scoreInTestingMe);
			}
			else{
				distance = Const.CONST_PENARTY;
			}
			
			//put the distance to mapForDistanceOnNGramForMe
			if(mapForDistanceOnNGram.containsKey(distance)){
				int tmp = mapForDistanceOnNGram.get(distance);
				mapForDistanceOnNGram.put(distance, tmp+1);
			}
			else{
				mapForDistanceOnNGram.put(distance, 1);
			}
		}
		return mapForDistanceOnNGram;
	}
	
	public void createMapForDistanceOnNGramForMe(){
		this.mapForDistanceOnNGramForMe = createMapForDistanceOnNGram(this.testingModelForMe);
	}

	public void displayMapForDistanceOnNGramForMe(String outputFileName){
		displayMap(this.mapForDistanceOnNGramForMe,outputFileName);
	}
	
	public void createMapForDistanceOnNGramForOther(){
		this.mapForDistanceOnNGRamForOther = createMapForDistanceOnNGram(this.testingModelForOther);
	}
	
	public void displayMapForDistanceOnNGramForOther(String outputFileName){
		displayMap(this.mapForDistanceOnNGRamForOther,outputFileName);
	}
	
	
	
	
	
	
	public void displayMap(TreeMap<Integer, Integer> map, String outputFileName){
		try {
			BufferedWriter bw = new BufferedWriter(new FileWriter(outputFileName));
			
			Iterator ite = map.entrySet().iterator();
			while(ite.hasNext()){
				Map.Entry entry = (Map.Entry)ite.next();
				int key = (Integer)entry.getKey();
				int value = (Integer)entry.getValue();
				bw.write(key+","+value);
				bw.newLine();
			}
			bw.flush();
			bw.close();
		} catch (Exception e) {
			// TODO: handle exception
		}
	}
	

	
	
	
	
	private TreeMap<Integer,Integer> createMapForDistanceOnFrequency(IDSModelBuilderLogic testingModel){
		TreeMap<Integer, Integer> mapForDistanceOnFrequency = new TreeMap<Integer, Integer>();
		
		//get listForEventScoreInTimeBin
		LinkedList<LinkedList<Integer>> listForEventScoreInTimeBinTRAINING_MODEL = this.trainingModel.getListForEventScoreInTimeBin();
		LinkedList<String> uniqueEventListTRAINING_MODEL = this.trainingModel.getUniqueEventNameList();
		LinkedList<LinkedList<Integer>> listForEventScoreInTimeBinTESTING_ME = testingModel.getListForEventScoreInTimeBin();
		LinkedList<String> uniqueEventListTESTING_ME = testingModel.getUniqueEventNameList();
		
		for(int i=0;i<24;i++){
			LinkedList<Integer> scoreListAtTimeME = listForEventScoreInTimeBinTESTING_ME.get(i);
			LinkedList<Integer> scoreListAtTimeMODEL = listForEventScoreInTimeBinTRAINING_MODEL.get(i);
			if(isContainValue(scoreListAtTimeME)){
				for(int j=0;j<scoreListAtTimeME.size();j++){
					String procName = uniqueEventListTESTING_ME.get(j);
					int scoreForMe = scoreListAtTimeME.get(j);
					//cal distance
					int distance = 0;
					if(uniqueEventListTRAINING_MODEL.contains(procName)){
						int index = uniqueEventListTRAINING_MODEL.indexOf(procName);
						int scoreForModel = scoreListAtTimeMODEL.get(index);
						distance = Math.abs(scoreForMe-scoreForModel);
					}
					else{
						distance = Const.CONST_PENARTY;
					}
					//put the distance to mapForDistanceOnFrequencyForMe
					if(mapForDistanceOnFrequency.containsKey(distance)){
						int tmp = mapForDistanceOnFrequency.get(distance);
						mapForDistanceOnFrequency.put(distance, tmp+1);
					}
					else{
						mapForDistanceOnFrequency.put(distance, 1);
					}
				}
			}
		}
		return mapForDistanceOnFrequency;
	}
	
	private boolean isContainValue(LinkedList<Integer> scoreList){
		Iterator<Integer> ite = scoreList.iterator();
		int total = 0;
		while(ite.hasNext()){
			total = total + ite.next();
		}
		if(total==0){
			return false;
		}
		else{
			return true;
		}
	}
	
	public void createMapForDistanceOnFrequencyForMe(){
		this.mapForDistanceOnFrequencyForMe = createMapForDistanceOnFrequency(this.testingModelForMe);
	}
	
	public void displayMapForDistanceOnFrequencyForMe(String outputFileName){
		displayMap(this.mapForDistanceOnFrequencyForMe, outputFileName);
	}
	
	public void createMapForDistanceOnFrequencyForOther(){
		this.mapForDistanceOnFrequencyForOther = createMapForDistanceOnFrequency(this.testingModelForOther);
	}
	
	public void displayMapForDistanceOnFrequencyForOther(String outputFileName){
		displayMap(this.mapForDistanceOnFrequencyForOther, outputFileName);
	}
	
	
	
	
	
	
	public void displayHistogramFreqForTrainingModel(String outputFileName){
		this.trainingModel.writeListForEventOccurrenceInTimeBin(outputFileName);
	}
	
	public void displayHistogramNGramTrainingModel(String outputFileName){
		this.trainingModel.writeMapForNGramScore(outputFileName);
	}
	
	public void displayHistogramFreqForTestingModelMe(String outputFileName){
		this.testingModelForMe.writeListForEventOccurrenceInTimeBin(outputFileName);
	}
	
	public void displayHistogramFreqForTestingModelOther(String outputFileName){
		this.testingModelForOther.writeListForEventOccurrenceInTimeBin(outputFileName);
	}
	
	
	
	

	
	
	
	
	/*
	 * main methods
	 */
	public static void main(String args[]){
		IDSAnomalyDetector ad = new IDSAnomalyDetector();
		ad.setTrainingData("traininglog");
		ad.setTestingDataForMe("testlog_me");
		ad.setTestingDataForOther("testlog_other");
		
		ad.createMapForDistanceOnNGramForMe();
		ad.displayMapForDistanceOnNGramForMe("out/ngram_distance_me.csv");
		ad.createMapForDistanceOnFrequencyForMe();
		ad.displayMapForDistanceOnFrequencyForMe("out/freq_distance_me.csv");
		
		ad.createMapForDistanceOnNGramForOther();
		ad.displayMapForDistanceOnNGramForOther("out/ngram_distance_other.csv");
		ad.createMapForDistanceOnFrequencyForOther();
		ad.displayMapForDistanceOnFrequencyForOther("out/freq_distance_other.csv");
		
		ad.displayHistogramFreqForTrainingModel("out/histogram_training_freq.csv");
		ad.displayHistogramNGramTrainingModel("out/histogram_training_ngram.csv");
		ad.displayHistogramFreqForTestingModelMe("out/histogram_testing_freq_me.csv");
		ad.displayHistogramFreqForTestingModelOther("out/histogram_testing_freq_other.csv");
	}
}

